package api

import (
	"net/http"

	"gitee.com/mkwchecking/sr-core/domain/service/srvctx"
	"gitee.com/mkwchecking/sr-core/log"
	"gitee.com/mkwchecking/sr-core/yerr"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/google/uuid"
)

var _ IApiCtx = (*ApiCtx)(nil)

func NewCtx(c *gin.Context) IApiCtx {
	return new(ApiCtx).WithGin(c)
}

type ApiCtx struct {
	g        *gin.Context
	req      any
	bindings []binding.Binding
	srvctx   *srvctx.Context
	next     Next
	logger   log.Logger
}

func (a *ApiCtx) Context() *srvctx.Context {
	return a.srvctx
}

func (a *ApiCtx) WithGin(g *gin.Context) IApiCtx {
	a.g = g

	traceId := uuid.NewString()
	g.Header(srvctx.TraceKey, traceId)
	g.Set(srvctx.TraceKey, traceId)

	a.logger = log.GetDefault().With("trace_id", traceId, "url", g.Request.URL.Path)

	a.srvctx = srvctx.New(g)
	a.srvctx.SetLogger(a.logger)

	return a
}

func (a *ApiCtx) WithLogger(logger log.Logger) IApiCtx {
	a.logger = logger.With("trace_id", a.srvctx.TraceId(), "url", a.g.Request.URL.Path)
	a.srvctx.SetLogger(logger)
	return a
}

// bind req
func (a *ApiCtx) WithReq(req any, binds ...binding.Binding) IApiCtx {
	a.req = req
	a.bindings = binds
	return a
}

func (a *ApiCtx) WithNext(next Next) IApiCtx {
	a.next = next
	return a
}

func (a *ApiCtx) Do() bool {

	if a.req != nil {
		if len(a.g.Params) > 0 {
			if err := a.g.ShouldBindUri(a.req); err != nil {
				a.writeError(nil, err, "")
				return false
			}
		}

		err := BindRequest(a.req, a.g, a.bindings...)
		if err != nil {
			a.writeError(nil, err, "")
			return false
		}
		err = VerifyRequest(a.req)
		if err != nil {
			a.writeError(nil, err, "")
			return false
		}
	}

	if a.next != nil {
		a.next(a, a.srvctx)

		if !a.g.IsAborted() {
			a.WriteOK(nil, "OK")
		}
	}
	return true
}

func (a *ApiCtx) WritePageOK(data any, total int64, pageNum, pageSize int, msg string) {
	a.writeOK(&PageData{
		List:     data,
		Total:    int(total),
		PageNum:  pageNum,
		PageSize: pageSize,
	}, msg)
}

func (a *ApiCtx) WriteOK(data any, msg string) {
	a.writeOK(data, msg)
}

func (a *ApiCtx) WriteCustom(data any, code int) {
	a.g.AbortWithStatusJSON(code, data)
}

func (a *ApiCtx) response() Responses {
	return NewResponse().
		SetTraceID(a.srvctx.TraceId())
}

func (a *ApiCtx) writeOK(data any, msg string) {
	resp := a.response().SetData(data).SetSuccess(true).SetMsg(msg)
	a.g.AbortWithStatusJSON(http.StatusOK, resp)
}

func (a *ApiCtx) WriteErr(data any, err error, msg string) {
	a.writeError(data, err, msg)
}
func (a *ApiCtx) writeError(data any, err error, msg string) {
	resp := a.response().SetSuccess(false).SetData(data)

	switch e := err.(type) {
	case *yerr.YError:
		resp.SetCode(int(e.Code()))
		if err := e.Err(); err != nil {
			resp.SetError(err)
		}
		if msg == "" {
			msg = e.Msg()
		}
		resp.SetMsg(msg)
	default:
		resp.SetCode(http.StatusInternalServerError).SetError(err).SetMsg(msg)
	}

	a.g.AbortWithStatusJSON(http.StatusInternalServerError, resp)

}
